﻿/*
   Copyright 2011 Softisis

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using FOM.Impl;
using FOM.Interfaces;
using NUnit.Framework;

namespace TestUnits
{
    [TestFixture]
    public class ConditionalMappingTest
    {

        /// <summary>
        /// Test a condition on a root mapping object
        /// </summary>
        [Test]
        public void TestRootNumberCond()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA, ObjectA>("test");
            rootmap.UseCondition(new TestCondAnIdOnA(666, false)); // root mapping is done if a.anid == 666

            var s = new ObjectA();
            s.Anid = 555; // no map with this value

            var res=(ObjectA)mngr.ApplyMapping("test", s);
            Assert.IsNull(res);
            
            
            s.Anid = 666; // map will be done
            var res2 = (ObjectA)mngr.ApplyMapping("test", s);
            Assert.IsNotNull(res2);

            // Same root map, but with a given target object
            var d = new ObjectA();
            d.Chaine = "target object";
            s.Anid = 555; // no map with ths value
            var res3 = (ObjectA)mngr.ApplyMapping("test", s, d);
            Assert.IsNull(res3); // yes! the source "d" is not returned, because condition is not fullfilled


            s.Anid = 666; // no map with ths value
            var res4 = (ObjectA)mngr.ApplyMapping("test", s, d);
            Assert.IsNotNull(res4);
            Assert.IsTrue(res4.Chaine == "target object"); // d is returned
        }



        /// <summary>
        /// Test a condition on a standard mapping property.
        /// It highlights taht sub mappings are realized, because condition
        /// is not recursive (but goes on sub mappings)
        /// </summary>
        [Test]
        public void TestPropNumberCond()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA, ObjectA>("test");
            rootmap.Map(a => a.UnObjetB.BUnObjetA).UseCondition(new TestCondAnIdOnA(333, false)) // BUnObjetA will be map if AnId=333
                .Map(ana => ana.Car); // this mapping WILL BE processed (Condtion is not recursive)

            var source = new ObjectA();
            source.UnObjetB=new ObjectB();
            source.UnObjetB.BUnObjetA=new ObjectA();
            source.UnObjetB.BUnObjetA.Chaine = "source object";
            source.UnObjetB.BUnObjetA.Car = 'X'; // will be wopied, wathever happen

            source.UnObjetB.BUnObjetA.Anid = 555; // no map with this value

            var res = (ObjectA)mngr.ApplyMapping("test", source);
            Assert.IsNotNull(res);
            Assert.IsNotNull(res.UnObjetB);
            Assert.IsNotNull(res.UnObjetB.BUnObjetA);
            Assert.IsTrue(res.UnObjetB.BUnObjetA.Car == 'X'); // YES! Even if mapping of BUnObjetA was not
            // done, the submapping on Car has triggered the creation of a new ObjectA, to fill it with car
            // But no other data comes from the source BUnObjetA:
            Assert.IsFalse(res.UnObjetB.BUnObjetA.Chaine == "source object"); // So ==> false

            // Now, trigger the source BUnObjetA to be mapped
            source.UnObjetB.BUnObjetA.Anid = 333; // map with this value

            var res2 = (ObjectA)mngr.ApplyMapping("test", source);
            Assert.IsNotNull(res2);
            Assert.IsNotNull(res2.UnObjetB);
            Assert.IsNotNull(res2.UnObjetB.BUnObjetA);
            Assert.IsTrue(res2.UnObjetB.BUnObjetA.Car == 'X'); // done by Car mapping AND ... BUnObjetA mapping:
            Assert.IsTrue(res2.UnObjetB.BUnObjetA.Chaine == "source object"); // True because cond was true

            // Now, sames things, but with an existing target object
            var tgt = new ObjectA();
            tgt.UnObjetB = new ObjectB();
            tgt.UnObjetB.BUnObjetA = new ObjectA();
            tgt.UnObjetB.BUnObjetA.Chaine = "target object"; // will change, according to the condition
            tgt.UnObjetB.BUnObjetA.Car = 'Z'; // will be replaced, wathever happen
            // Now, trigger the source BUnObjetA to be mapped
            source.UnObjetB.BUnObjetA.Anid = 555; // no map with this value

            var res3 = (ObjectA)mngr.ApplyMapping("test", source, tgt);
            Assert.IsNotNull(res3);
            Assert.IsNotNull(res3.UnObjetB);
            Assert.IsNotNull(res3.UnObjetB.BUnObjetA);
            Assert.IsTrue(res3.UnObjetB.BUnObjetA.Car == 'X'); // YES! Even if mapping of BUnObjetA was not
            // done, the submapping on Car has is realized. but the original tgt object is the same (not replaced because cond was false)
            Assert.IsTrue(res3.UnObjetB.BUnObjetA.Chaine == "target object"); // So ==> false

            // Now, trigger the source BUnObjetA to be mapped
            // we recreate object because altered from previous test
            tgt = new ObjectA();
            tgt.UnObjetB = new ObjectB();
            tgt.UnObjetB.BUnObjetA = new ObjectA();
            tgt.UnObjetB.BUnObjetA.Chaine = "target object"; // will change, according to the condition
            tgt.UnObjetB.BUnObjetA.Car = 'Z'; // will be replaced, wathever happen

            // Now, trigger the source BUnObjetA to be mapped
            source.UnObjetB.BUnObjetA.Anid = 333; // map with this value
            var res4 = (ObjectA)mngr.ApplyMapping("test", source, tgt);
            Assert.IsNotNull(res4);
            Assert.IsNotNull(res4.UnObjetB);
            Assert.IsNotNull(res4.UnObjetB.BUnObjetA);
            Assert.IsTrue(res4.UnObjetB.BUnObjetA.Car == 'X'); // mapped anyway
            Assert.IsTrue(res4.UnObjetB.BUnObjetA.Chaine == "source object"); // True because replace bay source because cond was true
        }

        /// <summary>
        /// Test a condition on a standard mapping property, with children ignored
        /// It highlights taht sub mappings are not realized, because condition
        /// is recursive
        /// </summary>
        [Test]
        public void TestPropNumberCondRecursiveActivated()
        {
            // create manager
            IManager mngr = new ManagerImpl();

            IMappingNode<ObjectA, ObjectA> rootmap = mngr.RegisterMapping<ObjectA, ObjectA>("test");
            rootmap.Map(a => a.UnObjetB.BUnObjetA).UseCondition(new TestCondAnIdOnA(333, true)) // BUnObjetA will be map if AnId=333
                .Map(ana => ana.Car); // this mapping WILL BE processed (Condtion is not recursive)

            var source = new ObjectA();
            source.UnObjetB = new ObjectB();
            source.UnObjetB.BUnObjetA = new ObjectA();
            source.UnObjetB.BUnObjetA.Chaine = "source object";
            source.UnObjetB.BUnObjetA.Car = 'X'; // will not wopied, because parent mapping is not validated

            source.UnObjetB.BUnObjetA.Anid = 555; // no map with this value

            var res = (ObjectA)mngr.ApplyMapping("test", source);
            Assert.IsNotNull(res);
            Assert.IsNotNull(res.UnObjetB);
            Assert.IsNull(res.UnObjetB.BUnObjetA); // YES!! because not any mapping is done, not even children

            // Now, trigger the source BUnObjetA to be mapped
            source.UnObjetB.BUnObjetA.Anid = 333; // map with this value

            var res2 = (ObjectA)mngr.ApplyMapping("test", source);
            Assert.IsNotNull(res2);
            Assert.IsNotNull(res2.UnObjetB);
            Assert.IsNotNull(res2.UnObjetB.BUnObjetA);
            Assert.IsTrue(res2.UnObjetB.BUnObjetA.Car == 'X'); // done by Car mapping AND ... BUnObjetA mapping:
            Assert.IsTrue(res2.UnObjetB.BUnObjetA.Chaine == "source object"); // True because cond was true

            // Now, sames things, but with an existing target object
            var tgt = new ObjectA();
            tgt.UnObjetB = new ObjectB();
            tgt.UnObjetB.BUnObjetA = new ObjectA();
            tgt.UnObjetB.BUnObjetA.Chaine = "target object"; // will change, according to the condition
            tgt.UnObjetB.BUnObjetA.Car = 'Z'; // will not replaced, because parent cond is not
            // Now, trigger the source BUnObjetA to be mapped
            source.UnObjetB.BUnObjetA.Anid = 555; // no map with this value

            var res3 = (ObjectA)mngr.ApplyMapping("test", source, tgt);
            Assert.IsNotNull(res3);
            Assert.IsNotNull(res3.UnObjetB);
            Assert.IsNotNull(res3.UnObjetB.BUnObjetA);
            Assert.IsTrue(res3.UnObjetB.BUnObjetA.Car == 'Z'); // no change to teh target, because parent node did not map
            Assert.IsTrue(res3.UnObjetB.BUnObjetA.Chaine == "target object"); // So ==> no mapped 

            // Now, trigger the source BUnObjetA to be mapped
            // we recreate object because altered from previous test
            tgt = new ObjectA();
            tgt.UnObjetB = new ObjectB();
            tgt.UnObjetB.BUnObjetA = new ObjectA();
            tgt.UnObjetB.BUnObjetA.Chaine = "target object"; // will change, according to the condition
            tgt.UnObjetB.BUnObjetA.Car = 'Z'; // will be replaced, wathever happen

            // Now, trigger the source BUnObjetA to be mapped
            source.UnObjetB.BUnObjetA.Anid = 333; // map with this value
            var res4 = (ObjectA)mngr.ApplyMapping("test", source, tgt);
            Assert.IsNotNull(res4);
            Assert.IsNotNull(res4.UnObjetB);
            Assert.IsNotNull(res4.UnObjetB.BUnObjetA);
            Assert.IsTrue(res4.UnObjetB.BUnObjetA.Car == 'X'); // mapped anyway
            Assert.IsTrue(res4.UnObjetB.BUnObjetA.Chaine == "source object"); // True because replace bay source because cond was true
        }


        public class TestCondAnIdOnA:ICondition
        {
            private readonly int _v;
            private readonly bool _ignoreChild;

            public TestCondAnIdOnA(int v, bool ignoreChild)
            {
                _v = v;
                _ignoreChild = ignoreChild;
            }

            public bool CanApplyMapping(MappingContext ctx)
            {
                if (ctx.ObjectSrc.GetType()!=typeof(ObjectA))
                    return false;
                var a = (ObjectA) ctx.ObjectSrc;
                return a.Anid == _v;
            }

            public bool IgnoreChildMappings
            {
                get { return _ignoreChild; }
            }

            public ICondition Reverse()
            {
                return null;
            }
        }
    }
}
